package video

import (
	"gitee.com/kaylee595/gkaylee/kerr"
	"github.com/google/uuid"
	"gorm.io/gorm"
	"math/rand/v2"
	"time"
	"videoPlay/internal/commonModels"
	"videoPlay/internal/dal"
	"videoPlay/internal/dal/dalModels"
)

type Service struct {
	db *gorm.DB
}

func New(db *gorm.DB) *Service {
	return &Service{db: db}
}

func (s *Service) keywordScope(keyword string) func(*gorm.DB) *gorm.DB {
	return func(tx *gorm.DB) *gorm.DB {
		if keyword == "" {
			return tx
		}
		keywords := dal.LikeContain(keyword)
		videoTagQuery := s.db.Model(&dalModels.VideoTag{}).Select("video_id").Where("content LIKE ?", keywords)
		durationTagQuery := s.db.Model(&dalModels.DurationTag{}).Select("video_id").Where("content LIKE ?", keywords)
		tx = tx.Where("id IN (?) OR id IN (?) OR name LIKE ?", videoTagQuery, durationTagQuery, keywords)
		return tx
	}
}

// GetVideoList 获取视频列表
// 关键词范围: 标签, 时间收藏备注, 视频标题.
// 支持分页获取.
func (s *Service) GetVideoList(keyword string, pager *commonModels.Pager) ([]*dalModels.Video, error) {
	var videos []*dalModels.Video
	tx := s.db.Preload("VideoTags")
	tx = tx.Preload("DurationTags", func(db *gorm.DB) *gorm.DB {
		return db.Order("second")
	})
	tx = tx.Order("record_at DESC")
	tx = tx.Scopes(s.keywordScope(keyword)) // 关键词范围限定
	if pager != nil {                       // 分页限定
		// 获取数据总量
		counter := new(int64)
		if err := tx.Model(&dalModels.Video{}).Count(counter).Error; err != nil {
			return nil, err
		}
		pager.Total = uint(*counter)
		// 限定分页范围
		tx.Scopes(pager.Scope)
	}
	if err := tx.Find(&videos).Error; err != nil {
		return nil, kerr.WrapTraceStack(err)
	}
	return videos, nil
}

func (s *Service) GetVideoById(id uuid.UUID) (*dalModels.Video, error) {
	video := new(dalModels.Video)
	if err := s.db.First(&video, id).Error; err != nil {
		return nil, kerr.WrapTraceStack(err)
	}
	return video, nil
}

// GetVideoByRandom 随机获取一个视频
// 关键词范围: 标签, 时间收藏备注, 视频标题.
func (s *Service) GetVideoByRandom(keyword string) (*dalModels.Video, error) {
	video := new(dalModels.Video)
	tx := s.db.Preload("VideoTags")
	tx = tx.Preload("DurationTags", func(db *gorm.DB) *gorm.DB {
		return db.Order("second")
	})
	tx = tx.Order("record_at DESC")
	tx = tx.Scopes(s.keywordScope(keyword)) // 关键词范围限定
	// 获取数据总量
	counter := new(int64)
	if err := tx.Model(&dalModels.Video{}).Count(counter).Error; err != nil {
		return nil, kerr.WrapTraceStack(err)
	}
	switch *counter {
	case 0:
		return nil, kerr.WrapTraceStack(gorm.ErrRecordNotFound)
	case 1:
		if err := tx.First(video).Error; err != nil {
			return nil, kerr.WrapTraceStack(err)
		}
	default:
		offset := rand.IntN(int(*counter))
		if err := tx.Offset(offset).First(video).Error; err != nil {
			return nil, kerr.WrapTraceStack(err)
		}
	}
	return video, nil
}

func (s *Service) DeleteVideoById(id uuid.UUID, hard bool) error {
	tx := s.db.Where("id = ?", id)
	if hard {
		tx = tx.Unscoped()
	}
	if err := tx.Delete(&dalModels.Video{}).Error; err != nil {
		return kerr.WrapTraceStack(err)
	}
	return nil
}

// AddVideoTag 添加视频标签
func (s *Service) AddVideoTag(video *dalModels.Video, content string) error {
	if err := s.db.Model(video).Association("VideoTags").Append(&dalModels.VideoTag{Content: content}); err != nil {
		return kerr.WrapTraceStack(err)
	}
	return nil
}

// AddDurationTag 添加时间标签
func (s *Service) AddDurationTag(video *dalModels.Video, second int64, content string) error {
	if err := s.db.Model(video).Association("DurationTags").Append(&dalModels.DurationTag{Second: second, Content: content}); err != nil {
		return kerr.WrapTraceStack(err)
	}
	return nil
}

// AddVideo 添加视频记录
func (s *Service) AddVideo(id uuid.UUID, recordAt time.Time, name, src, previewImgSrc, previewVideoSrc string) (*dalModels.Video, error) {
	video := &dalModels.Video{
		RecordAt:        recordAt,
		Id:              id,
		Name:            name,
		Src:             src,
		PreviewImgSrc:   previewImgSrc,
		PreviewVideoSrc: previewVideoSrc,
	}
	if err := s.db.Create(video).Error; err != nil {
		return nil, kerr.WrapTraceStack(err)
	}
	return video, nil
}

// SaveVideo 保存视频信息
func (s *Service) SaveVideo(video *dalModels.Video) error {
	if err := s.db.Session(&gorm.Session{FullSaveAssociations: true}).Save(video).Error; err != nil {
		return kerr.WrapTraceStack(err)
	}
	return nil
}
